package meshToSolid;

import grid.Grid3DFloat;
import hdgeom.primitives.Box;
import mmesh.MMesh;
import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PGraphics;
import simpleGeom.*;
import simpleGui.*;
import solid.SimpleMCube;

public class CopyOfVoxelization extends PApplet {
	String path = "/Users/dillenburger_b/Documents/Projects/2014 Grotto2/121ac/";
	String pathObject = path + "120acSolid.obj";
	String pathValue = pathObject.replace(".obj", ".txt");
	String pathMCubeOut =  pathObject.replace(".obj", "vox.obj");

	boolean scanForm = false;
	float dimGrid = 0.01f;// size of voxels in meters

	MMesh mesh;
	Box boundsObject; // Boundingbox of objectfile
	Box boundsInside; // Boundingbox defining voxelized part
	SimpleMCube mc = new SimpleMCube();
	//PCamera3D cam;
	Grid3DFloat grid;
	int displayMode = 0;
	Gui gui;
	GuiSpinnerGroup sx1, sx2, sy1, sy2, sz1, sz2;
	boolean flagUpdateInsideOutside;
	boolean flagExport=false;
	float posX, posY, posZ = 0;
	
	float iso=dimGrid*0.85f;
	PeasyCam cam;

	public void setup() {
		posX=0.2f;
		size(1000, 600, P3D);
		
		setupGui();
		cam = new PeasyCam(this,500);
		
		voxelize(scanForm);

	}

	public void setupGui() {
		int py = 20;
		int pyO = 10;
		int x1 = 10;
		int x2 = 50;
		gui = new Gui(this, this.createFont("Arial", 12));
		gui.addGuiLabel(x1, py + pyO, "x1x2: ");
		sx1 = gui.addGuiSpinnerGroup(x2, py, 30, 0, 999);
		py += 25;
		sx2 = gui.addGuiSpinnerGroup(x2, py, 830, 0, 999);
		py += 25;
		gui.addGuiLabel(x1, py + pyO, "y1: ");
		sy1 = gui.addGuiSpinnerGroup(x2, py, 160, 0, 999);
		py += 25;
		gui.addGuiLabel(x1, py + pyO, "y2: ");
		sy2 = gui.addGuiSpinnerGroup(x2, py, 880, 0, 999);
		py += 25;
		gui.addGuiLabel(x1, py + pyO, "z1: ");
		sz1 = gui.addGuiSpinnerGroup(x2, py, 130, 0, 999);
		py += 25;
		gui.addGuiLabel(x1, py + pyO, "z2: ");
		sz2 = gui.addGuiSpinnerGroup(x2, py, 980, 0, 999);

	}

	public void loadOBJ() {
		mesh = new MMesh();
		mesh.loadOBJ(pathObject);
		mesh.triangulateQuads();
		boundsObject = mesh.getBounds();
		println("bounds: "+boundsObject);
		boundsObject = boundsObject.getOffsetBox(dimGrid * 2);
	}

	public void voxelize(boolean doScan) {
		if (doScan) {
			loadOBJ();
			scan(dimGrid);
			grid.saveValues(pathValue);
		} else {
			loadOBJ();
			grid = new Grid3DFloat(pathValue);
			boundsInside = new Box(grid.nX * 0.2f, grid.nY * 0.2f,
					grid.nZ * 0.2f, grid.nX * 0.8f, grid.nY * 0.8f,
					grid.nZ * 0.9f);
		}
		defineInsideOutside(posX, posY, posZ);
		if (!flagExport){
		mc.marchingCubes(grid.nX, grid.nY, grid.nZ, grid.values,
				grid.dim * 0.85f, grid.dim, grid.x1, grid.y1, grid.z1, null);
		}
		else{
			mc.marchingCubes(grid.nX, grid.nY, grid.nZ, grid.values,
					grid.dim * 0.85f, grid.dim, grid.x1, grid.y1, grid.z1, path+timeStamp()+".obj");
			flagExport=false;
		}
	}

	public void scan(float dim) {
		float dimX = boundsObject.getDimX();
		float dimY = boundsObject.getDimY();
		float dimZ = boundsObject.getDimZ();
		int nX = (int) (dimX / dim) + 2;
		int nY = (int) (dimY / dim) + 2;
		int nZ = (int) (dimZ / dim) + 2;
		println("nX: " + nX + "  nY: " + nY + "  nZ: " + nZ);
		grid = new Grid3DFloat(nX, nY, nZ);
		grid.x1 = boundsObject.x1;
		grid.y1 = boundsObject.y1;
		grid.z1 = boundsObject.z1;
		grid.dim = dim;
		// scanning
		MMesh.scanMeshDistance(mesh.xc, mesh.yc, mesh.zc, mesh.triangles,
				boundsObject.x1, boundsObject.y1, boundsObject.z1, nX, nY, nZ,
				dim, dim * 1.6f, grid.values);
	}

	public void defineInsideOutside(float xPos, float yPos, float zPos) {
		boundsInside = new Box(grid.nX * sx1.getValue() / 1000f, grid.nY
				* sy1.getValue() / 1000f, grid.nZ * sz1.getValue() / 1000f,
				grid.nX * sx2.getValue()/ 1000f, grid.nY * sy2.getValue()
						/ 1000f, grid.nZ * sz2.getValue() / 1000f);
		grid = new Grid3DFloat(pathValue);
		for (int x = 0; x < grid.nX; x++) {
			for (int y = 0; y < grid.nY; y++) {
				for (int z = 0; z < grid.nZ; z++) {
					if (boundsInside.x1 > x || boundsInside.x2 < x
							|| boundsInside.y1 > y || boundsInside.y2 < y
							|| boundsInside.z1 > z || boundsInside.z2 < z) {
						grid.values[x * grid.nY * grid.nZ + y * grid.nZ + z] = 0;
					} else {
						if (grid.values[x * grid.nY * grid.nZ + y * grid.nZ + z] < 0) {
							grid.values[x * grid.nY * grid.nZ + y * grid.nZ + z] *= -1;
						}
					}
				}
			}
		}

		println("start reach");
		int iX = grid.getXInt(xPos);
		int iY = grid.getYInt(yPos);
		int iZ = grid.getZInt(zPos);
		int index = grid.getIndex(iX, iY, iZ);
		grid.reached(index, iso);
		/*Grid3DFloat.reached(grid.values, grid.nX, grid.nY, grid.nZ, index,
				iso);*/
		println("end reach");

		for (int i = 0; i < grid.values.length; i++) {
			if (grid.values[i] < 0)
				grid.values[i] *= -1;
			else if (grid.values[i] > iso)
				grid.values[i] = 0;
		}

		for (int x = 0; x < grid.nX; x++) {
			for (int y = 0; y < grid.nY; y++) {
				for (int z = 0; z < grid.nZ; z++) {
					if (boundsInside.x1 > x || boundsInside.x2 < x
							|| boundsInside.y1 > y || boundsInside.y2 < y
							|| boundsInside.z1 > z || boundsInside.z2 < z) {
						grid.values[x * grid.nY * grid.nZ + y * grid.nZ + z] = grid.dim * 2;
					}
				}
			}
		}
	}

	public void draw() {
		boundsInside = new Box(grid.nX * sx1.getValue() / 1000f, grid.nY
				* sy1.getValue() / 1000f, grid.nZ * sz1.getValue() / 1000f,
				grid.nX * (sx2.getValue()) / 1000f, grid.nY * sy2.getValue()
						/ 1000f, grid.nZ * sz2.getValue() / 1000f);
		if (flagUpdateInsideOutside) {
			flagUpdateInsideOutside = false;
			defineInsideOutside(posX, posY, posZ);
			mc.marchingCubes(grid.nX, grid.nY, grid.nZ, grid.values,
					grid.dim * 0.85f, grid.dim, grid.x1, grid.y1, grid.z1, null);
		}
		if (flagExport) {
			flagExport = false;
			mc.saveObj(path+timeStamp()+".obj");
		}
		drawData();
		cam.beginHUD();
		noLights();
		drawGui();
		cam.endHUD();
	}

	public void drawData() {
		background(0);
		pushMatrix();
		//cam.apply((PGraphics) g);
		rotateY(PI);
		hint(ENABLE_DEPTH_TEST);
		pointLight(170, 170, 170, 10, 10, 10);
		pointLight(170, 170, 170, -10, 10, -10);
		PCamera3D.drawWorldAxis3D(g, 100);
		float dimX = grid.x2() - grid.x1();
		scale(3 / dimX*10);
		if (displayMode == 0) {
			noFill();
			stroke(0, 255, 0);
			// mesh.draw((PGraphics3D) g);
			/*beginShape(TRIANGLES);
			for (int i = 0; i < mesh.triangles.length; i += 3) {
				for (int j = 0; j < 3; j++) {
					vertex(mesh.xc[mesh.triangles[i + j]],
							mesh.yc[mesh.triangles[i + j]],
							mesh.zc[mesh.triangles[i + j]]);
				}
			}
			endShape();*/
		}
		noStroke();
		fill(255);
		mc.draw((PGraphics) g);
		noFill();
		stroke(0, 0, 255);
		translate(grid.x1, grid.y1, grid.z1);
		scale(grid.dim);
		translate(boundsInside.getCenterX(), boundsInside.getCenterY(),
				boundsInside.getCenterZ());
		box(boundsInside.getDimX(), boundsInside.getDimY(),
				boundsInside.getDimZ());
		popMatrix();
	}

	public void drawGui() {
		noLights();
		hint(DISABLE_DEPTH_TEST);
		fill(10);
		stroke(100);
		rect(0, 0, 100, height);
		gui.display(g);
	}
	// returns a string encoding the current time
	public String timeStamp() {
	  return year() + nf(month(), 2) + nf(day(), 2) + "-" + nf(hour(), 2) + nf(minute(), 2) + nf(second(), 2);
	}
	public void keyPressed() {
		if (key == 'u') {
			flagUpdateInsideOutside = true;
		}
		if (key == 'd') {
			displayMode++;
			displayMode = displayMode % 3;
		}
		if (key == 'e') {
			flagExport = true;
		}
	}
}
